package com.simafei.flow.web.domain.graph;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.simafei.flow.core.common.NodeType;
import com.simafei.flow.web.domain.req.VariableReq;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * @author fengpengju
 */
public class GraphNodeDeserializer extends JsonDeserializer<GraphNode<?>> {

    public static final GraphNodeDeserializer INSTANCE = new GraphNodeDeserializer();

    @Override
    @SuppressWarnings("unchecked")
    public GraphNode<?> deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
        ObjectCodec codec = jsonParser.getCodec();

        JsonNode node = context.readTree(jsonParser);
        NodeType nodeType = NodeType.valueOf(node.get("nodeType").textValue());
        return switch (nodeType) {
            case END -> codec.treeToValue(node, EndNode.class);
            case API -> codec.treeToValue(node, ApiNode.class);
            case RULE -> codec.treeToValue(node, RuleNode.class);
            case DATA_AGG -> codec.treeToValue(node, AggNode.class);
            case DATA_LOAD -> codec.treeToValue(node, DataNode.class);
            case DATA_MODIFY -> codec.treeToValue(node, StoreNode.class);
            default -> {
                GraphNode<?> graphNode = new GraphNode<>();
                graphNode.setNodeType(nodeType);
                setValueIfNotEmpty(node, "id", jsonNode -> graphNode.setId(jsonNode.textValue()));
                setValueIfNotEmpty(node, "name", jsonNode -> graphNode.setName(jsonNode.textValue()));
                setValueIfNotEmpty(node, "blocked", jsonNode -> graphNode.setBlocked(jsonNode.booleanValue()));
                JsonNode jsonNode = node.get("variables");
                if (Objects.nonNull(jsonNode)) {
                    List<VariableReq> variables = codec.readValue(jsonNode.traverse(), new TypeReference<>() {
                    });
                    graphNode.setVariables(variables);
                }
                Map<String, Object> payload = codec.treeToValue(node.get("payload"), Map.class);
                graphNode.setPayload(payload);
                yield graphNode;
            }
        };
    }

    private void setValueIfNotEmpty(JsonNode node, String key, Consumer<JsonNode> consumer) {
        JsonNode jsonNode = node.get(key);
        if (Objects.nonNull(jsonNode)) {
            consumer.accept(jsonNode);
        }
    }
}
